﻿using System;
using System.Collections.Generic;
using System.Threading;
namespace Common.OjbectPool
{
    public abstract class BaseGenericObjectPool<T>
    {
        protected readonly Object closeLock = new Object();
        protected volatile bool closed;
        private readonly IEvictionPolicy<T> evictionPolicy;
        private readonly Timer evictor; // @GuardedBy("evictionLock")
        private readonly BaseObjectPoolConfig config;

        protected IEvictionPolicy<T> EvictionPolicy
        {
            get { return evictionPolicy; }
        }


        protected BaseObjectPoolConfig Config
        {
            get { return config; }

        }

        public ISwallowedExceptionListener SwallowedExceptionListener
        {
            get;
            set;
        }

        protected BaseGenericObjectPool(BaseObjectPoolConfig config)
            : this(config, new DefaultEvictionPolicy<T>())
        {

        }
        protected BaseGenericObjectPool(BaseObjectPoolConfig config, IEvictionPolicy<T> evictionPolicy)
        {
            this.config = config;
            this.evictionPolicy = evictionPolicy;
            evictor = new Timer(RunEvictor, null, config.TimeBetweenEvictionRunsMillis, Timeout.Infinite);
        }
        public bool IsClosed()
        {
            return closed;
        }
        public abstract void Close();

        protected abstract void Evict();

        protected abstract void EnsureMinIdle();

        protected void AssertOpen()
        {
            if (IsClosed())
            {
                throw new IllegalStateException("Pool not open");
            }
        }

        protected void StopEvictor()
        {
            evictor.Dispose();
        }

        protected void SwallowException(Exception e)
        {
            var listener = SwallowedExceptionListener;

            if (listener == null)
            {
                return;
            }

            try
            {
                listener.onSwallowException(e);
            }
            catch (OutOfMemoryException)
            {
                throw;
            }
            catch (SystemException)
            {
                throw;
            }
            catch (Exception)
            {
                // Ignore. Enjoy the irony.
            }
        }
        private void RunEvictor(object state)
        {
            // Evict from the pool
            try
            {
                Evict();
            }
            catch (OutOfMemoryException oome)
            {
                // Log problem but give evictor thread a chance to continue
                // in case error is recoverable
                Console.WriteLine(oome.StackTrace);

            }
            catch (Exception e)
            {
                SwallowException(e);
            }
            // Re-create idle instances.
            try
            {
                EnsureMinIdle();
            }
            catch (Exception)
            {

            }
            evictor.Change(config.TimeBetweenEvictionRunsMillis, Timeout.Infinite);
        }

    }
}
